package com.jzo2o.market.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jzo2o.common.model.PageResult;
import com.jzo2o.common.utils.*;
import com.jzo2o.market.enums.ActivityStatusEnum;
import com.jzo2o.market.mapper.ActivityMapper;
import com.jzo2o.market.model.domain.Activity;
import com.jzo2o.market.model.dto.request.ActivityQueryForPageReqDTO;
import com.jzo2o.market.model.dto.request.ActivitySaveReqDTO;
import com.jzo2o.market.model.dto.response.ActivityInfoResDTO;
import com.jzo2o.market.model.dto.response.SeizeCouponInfoResDTO;
import com.jzo2o.market.service.IActivityService;
import com.jzo2o.market.service.ICouponService;
import com.jzo2o.market.service.ICouponWriteOffService;
import com.jzo2o.mysql.utils.PageUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

import static com.jzo2o.market.constants.RedisConstants.RedisKey.ACTIVITY_CACHE_LIST;
import static com.jzo2o.market.constants.RedisConstants.RedisKey.COUPON_RESOURCE_STOCK;
import static com.jzo2o.market.enums.ActivityStatusEnum.*;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author itcast
 * @since 2023-09-16
 */
@Service
public class ActivityServiceImpl extends ServiceImpl<ActivityMapper, Activity> implements IActivityService {
	private static final int MILLION = 1000000;

	@Resource
	private RedisTemplate<String, Object> redisTemplate;

	@Resource
	private ICouponService couponService;

	@Resource
	private ICouponWriteOffService couponWriteOffService;

	@Override
	public PageResult<ActivityInfoResDTO> queryForPage(ActivityQueryForPageReqDTO activityQueryForPageReqDTO) {
		// 1.查询准备
		LambdaQueryWrapper<Activity> lambdaQueryWrapper = new LambdaQueryWrapper<>();
		// 查询条件
		lambdaQueryWrapper.eq(ObjectUtils.isNotNull(activityQueryForPageReqDTO.getId()),
				Activity::getId,
				activityQueryForPageReqDTO.getId()
			)
			.like(StringUtils.isNotEmpty(activityQueryForPageReqDTO.getName()),
				Activity::getName,
				activityQueryForPageReqDTO.getName()
			)
			.eq(ObjectUtils.isNotNull(activityQueryForPageReqDTO.getType()),
				Activity::getType,
				activityQueryForPageReqDTO.getType()
			)
			.eq(ObjectUtils.isNotNull(activityQueryForPageReqDTO.getStatus()),
				Activity::getStatus,
				activityQueryForPageReqDTO.getStatus()
			);

		// 排序
		lambdaQueryWrapper.orderByDesc(Activity::getId);
		// 分页
		Page<Activity> activityPage = new Page<>(activityQueryForPageReqDTO.getPageNo().intValue(),
			activityQueryForPageReqDTO.getPageSize().intValue()
		);
		activityPage = baseMapper.selectPage(activityPage, lambdaQueryWrapper);
		return PageUtils.toPage(activityPage, ActivityInfoResDTO.class);
	}

	@Override
	public ActivityInfoResDTO queryById(Long id) {
		// 1.获取活动
		Activity activity = baseMapper.selectById(id);
		// 判空
		if (activity == null) {
			return new ActivityInfoResDTO();
		}
		// 2.数据转换，并返回信息
		ActivityInfoResDTO activityInfoResDTO = BeanUtils.toBean(activity, ActivityInfoResDTO.class);
		// 设置状态
		//        activityInfoResDTO.setStatus(getStatus(activity.getDistributeStartTime(), activity.getDistributeEndTime(), activity.getStatus()));
		// 3.领取数量
		//        Integer receiveNum = couponService.countReceiveNumByActivityId(activity.getId());
		Integer receiveNum = activity.getTotalNum() - activity.getStockNum();
		activityInfoResDTO.setReceiveNum(receiveNum);
		// 4.核销量
		Integer writeOffNum = couponWriteOffService.countByActivityId(id);
		activityInfoResDTO.setWriteOffNum(NumberUtils.null2Zero(writeOffNum));

		//
		return activityInfoResDTO;
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public void save(ActivitySaveReqDTO activitySaveReqDTO) {
		// 1.逻辑校验
		activitySaveReqDTO.check();
		// 2.活动数据组装
		// 转换
		Activity activity = BeanUtils.toBean(activitySaveReqDTO, Activity.class);
		// 状态
		activity.setStatus(NO_DISTRIBUTE.getStatus());
		// 库存
		activity.setStockNum(activitySaveReqDTO.getTotalNum());
		if (activitySaveReqDTO.getId() == null) {
			activity.setId(IdUtils.getSnowflakeNextId());
		}
		// 排序字段
		//        long sortBy = DateUtils.toEpochMilli(activity.getDistributeStartTime()) * MILLION + activity.getId() % MILLION;
		// 3.保存
		saveOrUpdate(activity);
	}


	@Override
	public void updateStatus() {
		LocalDateTime now = DateUtils.now();
		// 1.更新已经进行中的状态
		lambdaUpdate()
			// 更新活动状态为进行中
			.set(Activity::getStatus, ActivityStatusEnum.DISTRIBUTING.getStatus())
			// 检索待生效的活动
			.eq(Activity::getStatus, NO_DISTRIBUTE.getStatus())
			// 活动开始时间小于等于当前时间
			.le(Activity::getDistributeStartTime, now)
			// 活动结束时间大于当前时间
			.gt(Activity::getDistributeEndTime, now).update();
		// 2.更新已经结束的
		lambdaUpdate()
			// 更新活动状态为已失效
			.set(Activity::getStatus, LOSE_EFFICACY.getStatus())
			// 检索待生效及进行中的活动
			.in(Activity::getStatus, Arrays.asList(DISTRIBUTING.getStatus(), NO_DISTRIBUTE.getStatus()))
			// 活动结束时间小于当前时间
			.lt(Activity::getDistributeEndTime, now).update();
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public void revoke(Long id) {
		// 1.活动作废
		boolean update = lambdaUpdate().set(Activity::getStatus, ActivityStatusEnum.VOIDED.getStatus())
			.eq(Activity::getId, id)
			.in(Activity::getStatus, Arrays.asList(NO_DISTRIBUTE.getStatus(), DISTRIBUTING.getStatus()))
			.update();
		if (!update) {
			return;
		}
		// 2.未使用优惠券作废
		couponService.revoke(id);

	}

	/**
	 * 活动预热
	 */
	@Override
	public void preHeat() {
		// 获取 30 天内的活动
		List<Activity> list = this.lambdaQuery().le(Activity::getDistributeStartTime, DateUtils.now().plusDays(30)).in(Activity::getStatus,
			NO_DISTRIBUTE.getStatus(),
			DISTRIBUTING.getStatus()
		).list();
		if (ObjectUtils.isEmpty(list)) {
			// 防止缓存穿透
			list = new ArrayList<>();
		}

		List<SeizeCouponInfoResDTO> seizeCouponInfoResDTO = BeanUtils.copyToList(list, SeizeCouponInfoResDTO.class);
		redisTemplate.opsForValue().set(ACTIVITY_CACHE_LIST, JsonUtils.toJsonStr(seizeCouponInfoResDTO));
		// 保存到 redis
		// 待开始的活动，直接存入 redis
		seizeCouponInfoResDTO.stream()
			.filter(dto -> getActivityNewStatus(dto.getStatus(), dto.getDistributeStartTime(), dto.getDistributeEndTime()) ==
				NO_DISTRIBUTE.getStatus())
			.forEach(v -> {
				Long activityId = v.getId();
				String key = String.format(COUPON_RESOURCE_STOCK, activityId % 10);
				redisTemplate.opsForHash().put(key, activityId, v.getStockNum());
			});
		// 进行中的活动，如果
		seizeCouponInfoResDTO.stream()
			.filter(dto -> getActivityNewStatus(dto.getStatus(), dto.getDistributeStartTime(), dto.getDistributeEndTime()) ==
				DISTRIBUTING.getStatus())
			.forEach(v -> {
				Long activityId = v.getId();
				String key = String.format(COUPON_RESOURCE_STOCK, activityId % 10);
				redisTemplate.opsForHash().putIfAbsent(key, activityId, v.getStockNum());
			});
	}

	/**
	 * 用户端查询抢卷获得列表
	 *
	 * @return:
	 * @param:
	 */
	@Override
	public List<SeizeCouponInfoResDTO> queryForListFromCache(Integer tabType) {
		// 从缓存中获取数据
		String json = (String) redisTemplate.opsForValue().get(ACTIVITY_CACHE_LIST);
		if (StringUtils.isEmpty(json)) {
			return List.of();
		}

		List<SeizeCouponInfoResDTO> dtoList = JsonUtils.toList(json, SeizeCouponInfoResDTO.class);

		// 过滤需要状态的活动
		int status = tabType == 1 ? DISTRIBUTING.getStatus() : NO_DISTRIBUTE.getStatus();

		return dtoList.stream().filter(dto -> status ==
			getActivityNewStatus(dto.getStatus(), dto.getDistributeStartTime(), dto.getDistributeEndTime())).peek(dto -> {
			dto.setStatus(status);
			dto.setRemainNum(dto.getStockNum());
		}).collect(Collectors.toList());
	}

	/**
	 * 计算活动的新状态
	 *
	 * @return:
	 * @param: status 原状态
	 * @param: distributeStartTime 活动开始时间
	 * @param: distributeEndTime 活动结束时间
	 */
	private int getActivityNewStatus(
		Integer status, LocalDateTime distributeStartTime, LocalDateTime distributeEndTime
	) {
		LocalDateTime now = DateUtils.now();
		// 原状态为未开始
		if (status == NO_DISTRIBUTE.getStatus()) {
			// 活动已开始
			if (now.isAfter(distributeStartTime) && now.isBefore(distributeEndTime)) {
				return DISTRIBUTING.getStatus();
			}
		}
		// 原活动状态为进行中
		if (status == DISTRIBUTING.getStatus()) {
			// 活动已失效
			if (now.isAfter(distributeEndTime)) {
				return LOSE_EFFICACY.getStatus();
			}
		}
		return status;
	}

	/**
	 * 从缓存中获取活动信息
	 *
	 * @return:
	 * @param:
	 */
	@Override
	public ActivityInfoResDTO getActivityInfoByIdFromCache(Long id) {
		// 1.从缓存中获取活动信息
		String json = (String) redisTemplate.opsForValue().get(ACTIVITY_CACHE_LIST);
		if (StringUtils.isEmpty(json)) {
			return null;
		}

		// 2.过滤指定活动信息
		List<ActivityInfoResDTO> dtoList = JsonUtils.toList(json, ActivityInfoResDTO.class);
		if (ObjectUtils.isEmpty(dtoList)) {
			return null;
		}

		// 3.过滤指定活动
		return dtoList.stream()
			.filter(dto -> dto.getStatus() == DISTRIBUTING.getStatus() && dto.getId().equals(id))
			.findFirst()
			.orElse(null);
	}

}
